package com.zhq.blog.core.domain;

import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.beanutils.BeanUtils;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;


@SuppressWarnings("rawtypes")
@MappedSuperclass
public class BasePersistentDomain<RepositoryType> extends BaseDomain  {
	
	@Id @GeneratedValue protected Long id;
	
	protected boolean deleted;
	
	@CreatedDate
	@Temporal(TemporalType.TIMESTAMP)
	protected Date createdDate;

	@LastModifiedDate
	@Temporal(TemporalType.TIMESTAMP)
	protected Date modifiedDate; 

	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public boolean isDeleted() {
		return deleted;
	}
			 
	public void setDeleted(boolean deleted) {
		this.deleted = deleted;
	}
	public Date getCreatedDate() {
		return createdDate;
	}
	public void setCreatedDate(Date createdDate) {
		this.createdDate = createdDate;
	}
	public Date getModifiedDate() {
		return modifiedDate;
	}
	public void setModifiedDate(Date modifiedDate) {
		this.modifiedDate = modifiedDate;
	}
	
	public static Specification<BasePersistentDomain<?>> notDeleted() {
        return new Specification<BasePersistentDomain<?>>() {
            @Override
            public Predicate toPredicate(Root<BasePersistentDomain<?>> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                return builder.not(root.<Boolean>get("isDeleted"));
            }
        };

	}
	public static Specification<BasePersistentDomain<?>> deleted() {
        return new Specification<BasePersistentDomain<?>>() {
            @Override
            public Predicate toPredicate(Root<BasePersistentDomain<?>> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                return builder.isTrue(root.<Boolean>get("isDeleted"));
            }
        };

	}
	
	@Override
	public boolean equals(Object other) {
		try{
			BasePersistentDomain<?> otherDomain=(BasePersistentDomain<?>) other;
			return this.getClass().getName().equals(other.getClass().getName()) && this.getId()==otherDomain.getId();
		}catch(Exception e){
			return false;
		}
	}
	
	/**
	 * 得到保存/更新成功后向用户提示的语句
	 * @return 保存/更新成功后向用户提示的语句
	 */
	public String getSucessFlashMessage(){
		String messagePrefix = (null==this.getId() ?"添加" : "修改");
		try {
			return messagePrefix + BeanUtils.getProperty(this, ("name")) + "成功。";
		} catch ( SecurityException e) {
            return messagePrefix + "成功。";
        }catch ( IllegalAccessException  e) {
            return messagePrefix + "成功。";
        }catch (  InvocationTargetException  e) {
            return messagePrefix + "成功。";
        }catch (  NoSuchMethodException e) {
            return messagePrefix + "成功。";
        }
	}

    public String getSucessFlashMessage(String message){
        String messagePrefix = (null==this.getId() ?"添加" : "修改");
        return messagePrefix + message + "成功";
    }
	
	/**
	 * 将controller拼装的domain转化为需要保存到数据库的domain,主要步骤为
	 * 1、在库中查出相同id的domain
	 * 2、遍历allowed filed，将自身允许修改的属性传给库中的domain
	 * 3、将库中的domain属性回传给自身
	 * 
	 * 本方法主要用于防止手动构建form的攻击，并解决createddate等隐藏参数，在页面保存后为空的情况
	 * @param fields controller中定义的allowed filed，使用@InitBinder定义。
	 */
	@SuppressWarnings("unchecked")
	public void merge(String[] fields){
		if(!this.isNew()){
			Object thisDomainInDB = null;
			try {
                thisDomainInDB = BeanUtils.cloneBean(((JpaRepository) getRepository()).findOne(this.getId()));
			} catch (IllegalAccessException e1) {
				logger.debug("error in copy merge a domain",e1);
			}catch (InstantiationException e1) {
                logger.debug("error in copy merge a domain",e1);
            }catch (InvocationTargetException e1) {
                logger.debug("error in copy merge a domain",e1);
            }catch (NoSuchMethodException e1) {
                logger.debug("error in copy merge a domain",e1);
            }
			for(String field : fields){
				try {
                    String property = BeanUtils.getProperty(this,field);
                    if(field.endsWith("Date") || field.endsWith("beginTime") ||  field.endsWith("endTime")) {
                        Date date = new SimpleDateFormat("EE MMM dd HH:mm:ss 'CST' yyyy", java.util.Locale.US).parse(property);
                        BeanUtils.copyProperty(thisDomainInDB, field, date);
                    }else {
                        BeanUtils.copyProperty(thisDomainInDB, field, property);
                    }
				} catch (IllegalAccessException  e) {
					logger.debug("error in copy merge a domain",e);
				} catch (InvocationTargetException e) {
                    logger.debug("error in copy merge a domain",e);
                } catch (NoSuchMethodException e) {
                    logger.debug("error in copy merge a domain",e);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
			try {
				BeanUtils.copyProperties(this, thisDomainInDB);
			} catch (IllegalAccessException  e) {
				logger.debug("error in copy merge a domain",e);
			}catch (InvocationTargetException e) {
                logger.debug("error in copy merge a domain",e);
            }
		}
	}	
	
	/**
	 * 取得当前Domain对应的Repository，Repository需要在context中定义，并声明类型在BasePersistentDomain<RepositoryType>中。
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public RepositoryType getRepository(){
		try {			
			return (RepositoryType)context.getClass().getMethod("get"+this.getClass().getSimpleName() + "Repository", new Class[0]).invoke(context, new Object[0]);		
		} catch (NoSuchMethodException e ) {
			logger.debug("error in getRepository",e);
		}catch ( SecurityException e ) {
            logger.debug("error in getRepository",e);
        }catch (IllegalArgumentException  e ) {
            logger.debug("error in getRepository",e);
        }catch (IllegalAccessException e ) {
            logger.debug("error in getRepository",e);
        }catch (InvocationTargetException e ) {
            logger.debug("error in getRepository",e);
        }
		return null;
	}
	
	/**
	 * 判断domain是否已保存过
	 * @return
	 */
	public boolean isNew(){
		return this.getId() == null || this.getId() == 0;
	}

}
